/**
 * 客户端用户模型
 *
 * Created by snowingsea on 2020/01/22.
 */
const crypto = require('crypto');
const randomstring = require('randomstring');
const request = require('request-promise');
const WebSocket = require('ws');
const querystring = require('querystring');
const apiURI = require('./../api/uri');
const authURI = require('./../api/auth/uri');
const users = require('./../../phoenix/models/users');
const { wsProtocol } = require('./../api/api-config');

class User {
  constructor(username, password = '') {
    this.username = username;
    this.password = crypto.createHash('sha256').update(password).digest('hex');

    this.userId = undefined;
    this.nickname = undefined;
    this.role = undefined;
    this.status = undefined;
    this.lang = undefined;

    this.wsURI = `${wsProtocol}${apiURI.substr(apiURI.indexOf(':'))}`;
    this.ws = null;
    this.token = {};
  }

  /**
   * 登录
   * @return {Promise}
   */
  login() {
    return new Promise((resolve, reject) => {
      this.logout()
        .then(() => this.getToken())
        .then(() => this.getMyInfo())
        .then(() => {
          this.ws = new WebSocket(`${this.wsURI}?${querystring.stringify(this.token)}`);
          let first = true;
          this.ws.on('close', () => {
            if (first) {
              first = false;
              reject(new Error('Failed to login: WebSocket closed'));
            }
          });
          this.ws.on('message', () => {
            if (first) {
              first = false;
              resolve();
            }
          });
          this.ws.on('error', (err) => {
            if (first) {
              first = false;
              this.ws.close();
              reject(err);
            }
          });
        })
        .catch(reject);
    });
  }

  /**
   * 登出
   * @return {Promise}
   */
  logout() {
    return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars
      if (this.ws) {
        this.ws.close(1000, 'Logout');
        this.ws = null;
      }
      resolve();
    });
  }

  /**
   * 获取token信息
   * @return {Promise}
   */
  getToken() {
    return new Promise((resolve, reject) => {
      request.post({
        url: `${authURI}/access-token.json`,
        form: this.getPasswordGrantForm(),
        json: true,
      })
        .then((token) => {
          this.token = token;
          resolve();
        })
        .catch(reject);
    });
  }

  /**
   * 获取个人信息
   * @return {Promise}
   */
  getMyInfo() {
    return new Promise((resolve, reject) => {
      request.get({
        url: `${apiURI}/me.json`,
        headers: { Authorization: `Bearer ${this.token.accessToken}` },
        json: true,
      })
        .then((res) => {
          const {
            userId,
            nickname,
            status,
            role,
            lang,
          } = res;
          this.userId = userId;
          this.nickname = nickname;
          this.status = status;
          this.role = role;
          this.lang = lang;
          resolve();
        })
        .catch(reject);
    });
  }

  /**
   * 获取密码授权表单数据
   * @return {Object}
   */
  getPasswordGrantForm() {
    return {
      grantType: 'password',
      username: this.username,
      password: this.password,
    };
  }

  /**
   * 获取随机用户信息
   * @return {Object}
   */
  static getRandomUserInfo() {
    let allRole = 0;
    const roleArray = [];
    Object.keys(users.roleKind).forEach((key) => {
      allRole |= users.roleKind[key]; // eslint-disable-line no-bitwise
    });
    for (let role = 0; role <= allRole; role += 1) {
      if ((role & users.roleKind.superUser) === 0) { // eslint-disable-line no-bitwise
        roleArray.push(role);
      }
    }
    const roles = [];
    let role = 0;
    const count = Math.floor(Math.random() * roleArray.length);
    for (let i = 0; i <= count && i < roleArray.length; i += 1) {
      const itemRole = roleArray[Math.floor(Math.random() * roleArray.length)];
      roles.push(itemRole);
      role |= itemRole; // eslint-disable-line no-bitwise
    }
    return {
      username: randomstring.generate(16),
      nickname: randomstring.generate(16),
      password: crypto.createHash('sha256').update(randomstring.generate(6)).digest('hex'),
      lang: randomstring.generate(5),
      roles,
      role,
      userHash: '',
      source: users.userSource.testGrid,
    };
  }
}

/**
 * Export model definition object.
 */
module.exports = User;
